#include "raft_utils/DataStream.h"

#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>

raft::utils::DataStream::DataStream() : pos_(0) {
    byteorder_ = byteorder();
}

raft::utils::DataStream::DataStream(const std::string &data) : pos_(0) {
    byteorder_ = byteorder();
    buffer_.clear();
    reserve(data.size());
    write(data.data(), data.size());
}

void raft::utils::DataStream::reserve(unsigned long long int len) {
    auto size = buffer_.size();
    auto cap = buffer_.capacity();
    if (size + len > cap) {
        while (size + len > cap) {
            if (cap == 0) {
                cap = 1;
            } else {
                cap *= 2;
            }
        }
        buffer_.reserve(cap);
    }
}

raft::utils::DataStream::ByteOrder raft::utils::DataStream::byteorder() {
    int n = 0x12345678;
    char str[4];
    memcpy(str, &n, sizeof(int));
    if (str[0] == 0x12) {
        return ByteOrder::BigEndian;
    } else {
        return ByteOrder::LittleEndian;
    }
}

void raft::utils::DataStream::show() const {
    auto size = buffer_.size();
    std::cout << "data size = " << size << "\n";
    int i = 0;
    while (i < size) {
        switch ((DataType) buffer_[i]) {
            case DataType::BOOL:
                if ((int) buffer_[++i] == 0) {
                    std::cout << "false";
                } else {
                    std::cout << "true";
                }
                ++i;
                break;
            case DataType::CHAR:
                std::cout << buffer_[++i];
                ++i;
                break;
            case DataType::INT32:
                std::cout << *((int32_t *) (&buffer_[++i]));
                i += 4;
                break;
            case DataType::INT64:
                std::cout << *((int64_t *) (&buffer_[++i]));
                i += 8;
                break;
            case DataType::FLOAT:
                std::cout << *((float *) (&buffer_[++i]));
                i += 4;
                break;
            case DataType::DOUBLE:
                std::cout << *((double *) (&buffer_[++i]));
                i += 8;
                break;
            case DataType::STRING:
                if ((DataType) buffer_[++i] == DataType::INT32) {
                    int len = *((int *) (&buffer_[++i]));
                    i += 4;
                    std::cout << std::string(&buffer_[i], len);
                    i += len;
                } else {
                    throw std::logic_error("parse string error");
                }
                break;
            case DataType::VECTOR:
                if ((DataType) buffer_[++i] == DataType::INT32) {
                    int len = *((int *) (&buffer_[++i]));
                    i += 4;
                } else {
                    throw std::logic_error("parse vector error");
                }
                break;
            case DataType::MAP:
                if ((DataType) buffer_[++i] == DataType::INT32) {
                    int len = *((int *) (&buffer_[++i]));
                    i += 4;
                } else {
                    throw std::logic_error("parse map error");
                }
                break;
            case DataType::SET:
                if ((DataType) buffer_[++i] == DataType::INT32) {
                    int len = *((int *) (&buffer_[++i]));
                    i += 4;
                } else {
                    throw std::logic_error("parse set error");
                }
                break;
            default:
                break;
        }
    }
    std::cout << "\n";
}

void raft::utils::DataStream::write(const char *data, unsigned long long int len) {
    reserve(len);
    auto size = buffer_.size();
    buffer_.resize(buffer_.size() + len);
    std::memcpy(&buffer_[size], data, len);
}

void raft::utils::DataStream::write(bool value) {
    char type = DataType::BOOL;
    write((char *) &type, sizeof(char));
    write((char *) &value, sizeof(char));
}

void raft::utils::DataStream::write(char value) {
    char type = DataType::CHAR;
    write((char *) &type, sizeof(char));
    write((char *) &value, sizeof(char));
}

void raft::utils::DataStream::write(int32_t value) {
    char type = DataType::INT32;
    write((char *) &type, sizeof(char));
    if (byteorder_ == ByteOrder::BigEndian) {
        char *first = (char *) &value;
        char *last = first + sizeof(int32_t);
        std::reverse(first, last);
    }
    write((char *) &value, sizeof(int32_t));
}

void raft::utils::DataStream::write(int64_t value) {
    char type = DataType::INT64;
    write((char *) &type, sizeof(char));
    if (byteorder_ == ByteOrder::BigEndian) {
        char *first = (char *) &value;
        char *last = first + sizeof(int64_t);
        std::reverse(first, last);
    }
    write((char *) &value, sizeof(int64_t));
}

void raft::utils::DataStream::write(float value) {
    char type = DataType::FLOAT;
    write((char *) &type, sizeof(char));
    if (byteorder_ == ByteOrder::BigEndian) {
        char *first = (char *) &value;
        char *last = first + sizeof(float);
        std::reverse(first, last);
    }
    write((char *) &value, sizeof(float));
}

void raft::utils::DataStream::write(double value) {
    char type = DataType::DOUBLE;
    write((char *) &type, sizeof(char));
    if (byteorder_ == ByteOrder::BigEndian) {
        char *first = (char *) &value;
        char *last = first + sizeof(double);
        std::reverse(first, last);
    }
    write((char *) &value, sizeof(double));
}

void raft::utils::DataStream::write(const char *value) {
    char type = DataType::STRING;
    write((char *) &type, sizeof(char));
    auto len = static_cast<int32_t>(strlen(value));
    write(len);
    write(value, len);
}

void raft::utils::DataStream::write(const std::string &value) {
    char type = DataType::STRING;
    write((char *) &type, sizeof(char));
    auto len = static_cast<int32_t>(value.size());
    write(len);
    write(value.data(), len);
}

void raft::utils::DataStream::write(const raft::utils::Serialize &value) {
    value.serialize(*this);
}

bool raft::utils::DataStream::read(char *data, int len) {
    std::memcpy(data, (char *) &buffer_[pos_], len);
    pos_ += len;
    return true;
}

bool raft::utils::DataStream::read(bool &value) {
    return false;
}

bool raft::utils::DataStream::read(char &value) {
    if (buffer_[pos_] != DataType::BOOL) {
        return false;
    }
    ++pos_;
    value = buffer_[pos_];
    ++pos_;
    return true;
}

bool raft::utils::DataStream::read(int32_t &value) {
    if (buffer_[pos_] != DataType::INT32) {
        return false;
    }
    ++pos_;
    value = *((int32_t *) (&buffer_[pos_]));
    if (byteorder_ == ByteOrder::BigEndian) {
        char *first = (char *) &value;
        char *last = first + sizeof(int32_t);
        std::reverse(first, last);
    }
    pos_ += 4;
    return true;
}

bool raft::utils::DataStream::read(int64_t &value) {
    if (buffer_[pos_] != DataType::INT64) {
        return false;
    }
    ++pos_;
    value = *((int64_t *) (&buffer_[pos_]));
    if (byteorder_ == ByteOrder::BigEndian) {
        char *first = (char *) &value;
        char *last = first + sizeof(int64_t);
        std::reverse(first, last);
    }
    pos_ += 8;
    return true;
}

bool raft::utils::DataStream::read(float &value) {
    if (buffer_[pos_] != DataType::FLOAT) {
        return false;
    }
    ++pos_;
    value = *((float *) (&buffer_[pos_]));
    if (byteorder_ == ByteOrder::BigEndian) {
        char *first = (char *) &value;
        char *last = first + sizeof(float);
        std::reverse(first, last);
    }
    pos_ += 4;
    return true;
}

bool raft::utils::DataStream::read(double &value) {
    if (buffer_[pos_] != DataType::DOUBLE) {
        return false;
    }
    ++pos_;
    value = *((double *) (&buffer_[pos_]));
    if (byteorder_ == ByteOrder::BigEndian) {
        char *first = (char *) &value;
        char *last = first + sizeof(double);
        std::reverse(first, last);
    }
    pos_ += 8;
    return true;
}

bool raft::utils::DataStream::read(std::string &value) {
    if (buffer_[pos_] != DataType::STRING) {
        return false;
    }
    ++pos_;
    int len;
    read(len);
    if (len < 0) {
        return false;
    }
    value.assign((char *) &(buffer_[pos_]), len);
    pos_ += len;
    return true;
}

bool raft::utils::DataStream::read(raft::utils::Serialize &value) {
    return value.deserialize(*this);
}

bool raft::utils::DataStream::readArgs() {
    return true;
}

const char *raft::utils::DataStream::data() const {
    return buffer_.data();
}

unsigned long long raft::utils::DataStream::size() const {
    return buffer_.size();
}

void raft::utils::DataStream::clear() {
    buffer_.clear();
}

void raft::utils::DataStream::reset() {
    pos_ = 0;
}

void raft::utils::DataStream::save(const std::string &filename) {
    std::ofstream fout(filename);
    fout.write(data(), static_cast<std::streamsize>(size()));
    fout.flush();
    fout.close();
}

void raft::utils::DataStream::load(const std::string &filename) {
    std::ifstream fin(filename);
    std::stringstream ss;
    ss << fin.rdbuf();
    const std::string &str = ss.str();
    buffer_.clear();
    reserve(str.size());
    write(str.data(), str.size());
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(bool value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(char value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(int32_t value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(int64_t value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(float value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(double value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(const char *value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(const std::string &value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator<<(const raft::utils::Serialize &value) {
    write(value);
    return *this;
}

template<typename T>
raft::utils::DataStream &raft::utils::DataStream::operator<<(const std::list<T> &value) {
    write(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator>>(bool &value) {
    read(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator>>(char &value) {
    read(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator>>(int32_t &value) {
    read(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator>>(int64_t &value) {
    read(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator>>(float &value) {
    read(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator>>(double &value) {
    read(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator>>(std::string &value) {
    read(value);
    return *this;
}

raft::utils::DataStream &raft::utils::DataStream::operator>>(raft::utils::Serialize &value) {
    read(value);
    return *this;
}
